(def intersect-line-line (l0 l1)
  "Lines intersection algorithm
   Args:
     l*: (cons -> cons -> num) => (cons -> (point point))
       '(point1 point2)
       where:
         point*: '(p*x p*y)
   Returns:
     (r px py)
     where:
       px py (num): intersection point
         r (t): real intersection"
  (with (((p0x p0y) (p1x p1y)) l0
         ((p2x p2y) (p3x p3y)) l1)
    (with (pend-ratio (- (* (- p3y p2y) (- p1x p0x))
                         (* (- p3x p2x) (- p1y p0y)))
           a (- (* (- p3x p2x) (- p0y p2y)) (* (- p3y p2y) (- p0x p2x)))
           b (- (* (- p1x p0x) (- p0y p2y)) (* (- p1y p0y) (- p0x p2x))))
      (when (no (is pend-ratio 0))
        (= a (/ a pend-ratio))
        (= b (/ b pend-ratio))
        (let real (and (<= 0 a 1) (<= 0 b 1))
          (list real
                (+ p0x (* a (- p1x p0x)))
                (+ p0y (* a (- p1y p0y)))))))))

(def intersect-circle-circle (c0 c1)
  "Circles intersection algorithm
   Args:
     c*: (cons -> cons num) => (cons -> point radii)
       '(center radii)
       where:
         center: '(cx cy)
   Returns:
     (p1 p2)
     where:
       p* (point): intersection point"
  (with ((p0 r0) c0
         (p1 r1) c1)
    (let d (point-dist p0 p1)
      (unless (or (> d (+ r0 r1))
                  (< d (abs:- r0 r1))
                  (and (is d 0)
                       (is r0 r1)))
        (let a (/ (+ (- (* r0 r0) (* r1 r1)) (* d d)) (* 2 d))
          (let h (sqrt:- (* r0 r0) (* a a))
            (let p2 (list (+ p0.0 (/ (* a (- p1.0 p0.0)) d))
                          (+ p0.1 (/ (* a (- p1.1 p0.1)) d)))
              (with (h3x (/ (* h (- p1.1 p0.1)) d)
                     h3y (/ (* h (- p1.0 p0.0)) d))
                (with (p3a (list (- p2.0 h3x)
                                 (+ p2.1 h3y))
                       p3b (list (+ p2.0 h3x)
                                 (- p2.1 h3y)))
                  (if (iso p3a p3b)
                      (list p3a)
                      (list p3a p3b)))))))))))

(def intersect-line-circle (line circle)
  "Line-circle intersection algorithm
   Args:
     line: (cons -> cons -> num) => (cons -> (point point))
       '(point1 point2)
       where:
         point*: '(p*x p*y)
     circle: (cons -> cons num) => (cons -> point radii)
       '(center radii)
       where:
         center: '(cx cy)
   Returns:
     ((p1x p1y r) (p2x p2y r))
     ((px py r)) ;; When line is tangent
     where:
       p* (point): intersection point
       r (t): real intersection"
  (with (((x1 y1) (x2 y2)) line
         ((x3 y3) r) circle)
    (with (a (+ (expt (- x2 x1) 2) (expt (- y2 y1) 2))
           b (* 2 (+ (* (- x2 x1) (- x1 x3)) (* (- y2 y1) (- y1 y3))))
           c (- (+ (* x3 x3) (* y3 y3) (* x1 x1) (* y1 y1))
                (* 2 (+ (* x3 x1) (* y3 y1))) (* r r)))
      (let x (- (* b b) (* 4 a c))
        (when (<= 0 x)
          (let roots (list (-:/ (+ (sqrt x) b) (* 2 a))
                           (/ (- (sqrt x) b) (* 2 a)))
            (let points nil
              (each root (if (apply iso roots) cdr.roots roots)
                (push (list (+ x1 (* root (- x2 x1)))
                            (+ y1 (* root (- y2 y1)))
                            (<= 0 root 1))
                      points)) points)))))))
